package com.ming_za.odupdaterserver.core.controller.web;

import com.ming_za.odupdaterserver.api.UpdateListener;
import com.ming_za.odupdaterserver.api.Updater;
import com.ming_za.odupdaterserver.core.config.ConfigManager;
import com.ming_za.odupdaterserver.core.config.type.IntValue;
import com.ming_za.odupdaterserver.core.exception.BusinessException;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.service.ReturnBusinessPackage;
import com.ming_za.odupdaterserver.core.service.res.obj.MainUpdateItem;
import com.ming_za.odupdaterserver.core.service.update.UpdateState;
import com.ming_za.odupdaterserver.core.service.update.res.ResDownload;
import com.ming_za.odupdaterserver.core.utils.FileUtils;
import com.ming_za.odupdaterserver.core.utils.Msg;
import com.ming_za.odupdaterserver.core.utils.NetworkUtils;
import com.ming_za.odupdaterserver.core.utils.VersionUtils;
import com.ming_za.odupdaterserver.core.vo.R;
import com.ming_za.odupdaterserver.core.vo.VoUtils;
import com.ming_za.odupdaterserver.core.vo.resp.ResInfoResp;
import com.ming_za.odupdaterserver.core.vo.resp.SettingResp;
import com.ming_za.odupdaterserver.core.vo.resp.VersionInfoResp;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Tag(name = "更新相关API")
@RestController
@RequestMapping("/sev")
public class UpdateAPI {

    private final Updater updater;
    private final FileUtils fileUtils;
    private final UpdateListener updateListener;
    private final VoUtils voUtils;

    private final FileUtils.NormalTransmitter normalTransmitter;
    private final FileUtils.LimitedTransmitter limitedTransmitter;

    private int maxLimit;
    private int concurrencyLimit;
    private int unpublishKey;

    private final IntValue maxConcurrentDownload;

    /**
     * 下载传输中的内容，方便阻断。(资源id,输入流)
     */
    @Getter
    private static final Map<Long, InputStream> transmission = new HashMap<>();

    public UpdateAPI(Updater updater, ConfigManager configManager, FileUtils fileUtils, UpdateListener updateListener, VoUtils voUtils, FileUtils.NormalTransmitter normalTransmitter, FileUtils.LimitedTransmitter limitedTransmitter) {
        this.updater = updater;
        this.fileUtils = fileUtils;
        this.updateListener = updateListener;
        this.voUtils = voUtils;
        this.normalTransmitter = normalTransmitter;
        this.limitedTransmitter = limitedTransmitter;

        IntValue intValue = configManager.getMaxSingleDownloadBroadband();
        maxLimit = intValue.getValue() * 131072;
        intValue.bindListener(value -> maxLimit = value * 131072);

        IntValue maxDownloadBroadband = configManager.getMaxDownloadBroadband();
        concurrencyLimit = maxDownloadBroadband.getValue() * 131072;
        maxDownloadBroadband.bindListener((v)-> concurrencyLimit = v * 131072);

        IntValue unpublishedSecretKey = configManager.getUnpublishedSecretKey();
        this.unpublishKey = unpublishedSecretKey.getValue();
        unpublishedSecretKey.bindListener((v)->{
            this.unpublishKey = v;
        });

        this.maxConcurrentDownload = configManager.getMaxConcurrentDownload();
    }

    @Parameters({
            @Parameter(name = "resId",description = "资源id",in = ParameterIn.PATH,required = true)
    })
    @Operation(summary = "获取更新资源，返回文件下载")
    @GetMapping("/resFile/{resId}")
    public R<?> res(HttpServletRequest request, HttpServletResponse response, @PathVariable("resId") Long resId) throws IOException {

        //TODO 当前不检测资源是否发布
        ReturnBusinessPackage<ResDownload> rbp = updater.updateRes(resId, request.getRemoteHost(), false);

        if (!rbp.isSuccess()){
            return R.pack(rbp.getCode());
        }

        ResDownload rd = rbp.getData();

        FileInputStream fis = rd.getFileInputStream();

        response.setContentType("application/octet-stream");
        response.setContentLengthLong(fis.available());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(rd.getFileName(), StandardCharsets.UTF_8));

        //传输限速
        try (OutputStream outputStream = response.getOutputStream()) {
            FileUtils.Transmitter transmitter;

            if (concurrencyLimit > 0) {
                transmitter = limitedTransmitter;
            } else {
                transmitter = normalTransmitter;
            }

            transmission.put(resId, rd.getFileInputStream());
            if (maxLimit > 0) {
                fileUtils.limitStreamTransmit(outputStream, fis, maxLimit, transmitter);
            } else {
                fileUtils.streamTransmit(outputStream, fis, transmitter);
            }
            rd.getRes().setState(UpdateState.DONE);
            outputStream.flush();
        } catch (IOException e){
            Msg.getInstance().warn("[资源下载][" + request.getRemoteHost() + "]意外中断！");
        } catch (Exception e) {
            rd.getRes().setState(UpdateState.ERROR);
            updateListener.error(rd.getRes(), request.getRemoteHost(), e);
            Msg.getInstance().warn("[资源下载]" + e.getMessage());
        } finally {
            InputStream is = transmission.remove(resId);
            if (is != null){
                is.close();
            }
        }
        updateListener.afterUpdate(rd.getRes(),request.getRemoteHost());
        return null;
    }

    private final String versionExplain = "版本号-二进制版本号,支持0.0.0.0~127.127.127.127，主版本号. 子版本号. 修订版本号. 构建版本号,格式:,byte[0] = 主版本号,byte[1] = 子版本号,byte[2] = 修订版本号,byte[3] = 构建版本号";


    @Operation(summary = "获取客户端版本到最新版之间的所有版本-返回版本列表")
    @GetMapping("/version")
    public R<List<Integer>> getResVersion(HttpServletRequest request, @Schema(description = versionExplain) int version,@Schema(description = "内测密钥，设为0不使用密钥") int key){

        Msg.getInstance().info("[" + request.getRemoteHost() + "]获取更新-客户端版本：" + VersionUtils.transformVersion(version) + ",内测密钥：" + key);

        if (key == 0){
            return R.ok(updater.getVersionBetweenClientVersion(version,true));
        }else if (key == unpublishKey){
            return R.ok(updater.getVersionBetweenClientVersion(version,false));
        }else {
//            throw new BusinessException(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
            return R.pack(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
        }
    }

    @Operation(summary = "获取版本资源")
    @GetMapping("/res")
    public R<List<MainUpdateItem>> getUpdateRes(
            @Schema(description = versionExplain) @RequestParam int version,
            @Schema(description = "标签") @RequestParam long label,
            @Schema(description = "内测密钥，设为0不使用密钥") @RequestParam int key,
            @Schema(description = "客户端当前支持服务器的版本") @RequestParam(required = false,defaultValue = "0") Integer s_version){
        if (key == 0){
            ReturnBusinessPackage<List<MainUpdateItem>> rbp = updater.getUpdateRes(version, label, true,s_version);
            return R.pack(rbp.getCode(),rbp.getData());
        }else if (key == unpublishKey){
//            return R.ok(updater.getUpdateRes(version,label,false));
            ReturnBusinessPackage<List<MainUpdateItem>> rbp = updater.getUpdateRes(version, label, false,s_version);
            return R.pack(rbp.getCode(),rbp.getData());
        }else {
//            throw new BusinessException(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
            return R.pack(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
        }
    }

    @Parameters({
            @Parameter(name = "resId",description = versionExplain,in = ParameterIn.PATH,required = true),
    })
    @Operation(summary = "获取资源详细信息")
    @GetMapping("/res/{resId}")
    public R<ResInfoResp> getUpdateRes(@PathVariable("resId") Long resId){
        return R.ok(voUtils.cover(updater.getResInfo(resId),new ResInfoResp()));
    }

    @Operation(summary = "最大下载并发")
    @GetMapping("/maxDownloadConcurrent")
    public R<SettingResp> maxDownloadConcurrent(){
        return R.ok(new SettingResp(maxConcurrentDownload.getValue()));
    }

    @Operation(summary = "获取未发布版本")
    @GetMapping("/unpublishVersion")
    public R<Integer> getUnpublishedVersion(@Schema(description = "内测密钥") int key){

        if (unpublishKey != key){
//            throw new BusinessException(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
            return R.pack(ROEnum.UPDATE_UNPUBLISHED_KEY_ERROR);
        }

        int version = 0;
        for (VersionInfoResp i : updater.getAllVersionInfo()){
            if (!i.isPublished()){
                version = i.getVersion();
                break;
            }
        }
        return R.ok(version);
    }

    @Operation(summary = "获取关于内容")
    @GetMapping(value = "/ui/about")
    public R<String> getAbout(){
        String result = "";
        try {
            result = NetworkUtils.sendGet("https://gitee.com/ming_za/odupdater-home/raw/master/about.txt",String.class);
        }catch (Exception ignored){

        }
        return R.ok(result);
    }

    @Operation(summary = "获取公告内容")
    @GetMapping(value = "/ui/announcement")
    public R<String> getAnnouncement(){
        String result = "";
        try {
            result = NetworkUtils.sendGet("https://gitee.com/ming_za/odupdater-home/raw/master/announcement.txt",String.class);
        }catch (Exception ignored){

        }
        return R.ok(result);
    }
}
